
KeypressMessage = function(key) {
    Message.call(this);
    this.key = key;
};
KeypressMessage.prototype = new ClientMessage;
KeypressMessage.prototype.signature = 'keypress';

KeypressMessage.prototype.as_json = function() {
    var result = ClientMessage.prototype.as_json.call(this);
    result.key = this.key;
    return result;
};

KeypressMessage.from_event = function(event) {
    return new KeypressMessage(event.keyIdentifier);
};

// document.addEventListener("keydown", KeypressMessage.from_event, false);

ButtonPushMessage = function(button_id) {
	ClientMessage.call(this);
    this.button_id = button_id;
};

ButtonPushMessage.prototype = new ClientMessage;
ButtonPushMessage.prototype.signature = 'button-push';

ButtonPushMessage.prototype.as_json = function() {
    var result = ClientMessage.prototype.as_json.call(this);
    result['source'] = this.button_id;
    return result;
};

ClientValueChangeMessage = function(visual_id, value) {
	ClientMessage.call(this);
    this.visual_id = visual_id;
    this.value = value;
};

ClientValueChangeMessage.prototype = new ClientMessage;
ClientValueChangeMessage.prototype.signature = 'value-change';

ClientValueChangeMessage.prototype.as_json = function() {
    var result = ClientMessage.prototype.as_json.call(this);
    result['visual-identifier'] = this.visual_id;
    result['value'] = this.value;
    return result;
};

AlertMessage = function(text) {
    ServerMessage.call(this);
    this.text = text;
};

AlertMessage.prototype = new ServerMessage;
AlertMessage.prototype.signature = 'alert';

AlertMessage.prototype.perform = function() {
    alert(result.text);
};

AlertMessage.prototype.build = function(payload) {
    return new AlertMessage(payload.text);
};

RefreshMessage = function(id, related) {
    ServerMessage.call(this, id, related);
};

RefreshMessage.prototype = new ServerMessage;
RefreshMessage.prototype.signature = 'refresh';

RefreshMessage.prototype.perform = function() {
    location.reload();
};
RefreshMessage.prototype.build = function(payload) {
    return new RefreshMessage();
};

VisualAppendMessage = function(parent_id, child_representation) {
    ServerMessage.call(this);
    this.parent_id = parent_id;
    this.child_representation = child_representation;
};

VisualAppendMessage.prototype = new ServerMessage;
VisualAppendMessage.prototype.signature = 'visual-append';

VisualAppendMessage.prototype.perform = function() {
    var parent = document.getElementById(this.parent_id);
    var child = Widget.create(this.child_representation);
    parent.appendChild(child);
};

VisualAppendMessage.prototype.build = function(payload) {
    return new VisualAppendMessage(payload['parent-identifier'], payload['child-representation']);
};

ServerValueChangeMessage = function(visual_id, value) {
    ServerMessage.call(this);
    this.visual_id = visual_id;
    this.value = value;
};
ServerValueChangeMessage.prototype = new ServerMessage;
ServerValueChangeMessage.prototype.signature = 'value-change';

ServerValueChangeMessage.prototype.perform = function() {
    var visual = document.getElementById(this.visual_id);
    visual.value = this.value;
};

ServerValueChangeMessage.prototype.build = function(payload) {
    return new ServerValueChangeMessage(payload['visual-identifier'], payload['value']);
};


Messages = {
    show: function(result) {
        var visual = document.getElementById(result.visual);
        Widget.replace(visual, result.widget);
    },
    hide: function(result) {
        var visual = document.getElementById(result.visual);
        Widget.replace(visual, {element: 'div', attributes: {id: result.visual}, content: []});
    },
    insert: function(result) {
        var container = document.getElementById(result.container);
        Widget.insert(container, result.visual, result.index);
    },
    remove: function(result) {
        var container = document.getElementById(result.container);
        Widget.remove(container, result.index);
    }
};

Widget = {
    create: function(element_data) {
    	var element = document.createElement(element_data['element-name']);
    	Widget.update(element, element_data);
        Behaviorizer.decorate(element);
        return element;
    },
    update: function(element, element_data) {
    	var attributes = element_data['attributes'];
        for(var key in attributes) {
            var value = attributes[key];
            element.setAttribute(key, value);
        }
        // TODO dont rewrite whole visual
        while(element.hasChildNodes()) {
            element.removeChild(element.firstChild);
        }
        var text = document.createTextNode(element_data['text']);
        element.appendChild(text);
        var children = element_data['children'];
        for(var i = 0; i < children.length; i++) {
            element.appendChild(Widget.create(children[i]));
        }
        return element;
    },
    /*
    replace: function(element, element_data) {
        var new_element = Widget.create(element_data);
        element.parentNode.replaceChild(new_element, element);
        return new_element;
    },

    insert: function(container, element_data, index) {
        var new_element = Widget.create(element_data);
        if(index < 0) {
            logging.error("Tried to insert child at negative position");
        }
        else if(index < container.children.length) {
            container.insertBefore(new_element, container.children.item(index));
        }
        else if(index === container.children.length) {
            container.appendChild(new_element);
        }
        else {
            logging.error("Tried to insert child at position far after end of child list");
        }
    },

    remove: function(container, index) {
        if(index < 0) {
            logging.error("Tried to remove child at negative position");
        }
        else if(index < container.children.length) {
            container.removeChild(container.children.item(index));
        }
        else {
            logging.error("Tried to remove child at position after end of child list");
        }
    }
    */
};

Behaviorizer = {
    decorate: function (element) {
        if(element.tagName == 'BUTTON') {
            Behaviorizer.pushable(element);
        }
        if(element.tagName == 'INPUT' && element.type == 'text') {
            Behaviorizer.editable(element);
        }
    },
    pushable: function (element) {
        element.onclick = function() {
            var message = new ButtonPushMessage(this.id);
            message.send();
            return false;
        };
    },
    editable: function (element) {
        element.onkeyup = function() {
            var message = new ClientValueChangeMessage(this.id, this.value);
            message.send();
        };
    }
};

init.execute(function () {
    var elements = document.getElementsByTagName('*');
    for(var i = 0; i < elements.length; i++) {
        Behaviorizer.decorate(elements[i]);
    }
});
